home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
demo
/
igps_102.zip
/
TALKSOCK.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-01
|
23KB
|
767 lines
///////////////////////////////////////////////////////////////////////////////////
// Internet Global Phone Project
// talksock.h : Implementation of the CTalkClient, CTalkListenServer, and CTalkServer
// classes
//
// The CTalk... classes implements protocol specific behaviours on top of the
// CSocketOwner, CSockServer, CSockClient, and CSockListenServer base classes.
// See the main text of article in Dr. Dobb's Journal for a discussion of the
// class hierachy. A discussion of the protocol can also be found in the text.
//
////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1993-1994 microWonders Inc. All rights reserved.
//
// AN OPEN INVITION TO BUILD UPON AND CONTRIBUTE TO THE PUBLIC TECHNOLOGY POOL:
// You are encouraged to redistribute, and build upon the technologies presented
// in this source module and the accompanying article in Dr. Dobb's Journal provided
// all the conditions listed in the MUSTREAD.TXT file, included with this
// distribution, are met.
////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "mmsystem.h"
#include "mphone.h"
#include "phonedoc.h"
#include "wsmin.h"
#include "socket.h"
#include "talksock.h"
#include "phonevw.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
////////////////////////////////////////////////////////////////////////////////
// CTalkServer Class
//
// Philosophy of Implementation: CONSERVATIVE
//
// terminate and destroy connection, socket, server, and memory on any error
//
// -->ensure integrity and security
//
// June 5, 1994 Fix up occasional lockup after send on some WINSOCKs, the first
// Recv on some WINSOCKs gives garbage upon connect.
//
// December 1, 1994 Disabled stringent error checking that caused the
// program to abort connection at the slightest excuse
// (PA)
//
CTalkServer::CTalkServer()
{
}
CTalkServer::~CTalkServer()
{
// Let the Phoneview free memory, since play maynot complete immediately.
// That is, the Server object may be destroyed before audio play completed.
}
BOOL CTalkServer::GetBuffer()
{
// This routine is called by the Listener when dynamically creating
// a Server object. The buffer is created to hold the transmitted
// compressed audio stream.
//
m_hspeechBuffer = GlobalAlloc(GHND , (DWORD) COMPRESSED_BUFSIZE);
if (!m_hspeechBuffer)
{
AfxGetMainWnd()->MessageBox("cannot allocate compress buffer!");
return (FALSE);
}
m_pBuffer = (HPSTR) GlobalLock(m_hspeechBuffer);
if (!m_pBuffer)
{
GlobalUnlock(m_hspeechBuffer);
GlobalFree(m_hspeechBuffer);
AfxGetMainWnd()->MessageBox("cannot lock compress buffer!");
return (FALSE);
}
m_pCurBuf = m_pBuffer; // set sliding ptr
return(TRUE);
}
void CTalkServer::OnOtherMsgs(int iEvent, int iError)
{
// override the 'catch all' virtual function to trap our special
// WSTALKBEGIN message. This message is sent as soon as the
// server thread comes alive.
//
// We start the first element of the protocol by sending an
// ACK.è if (iEvent == WSTALKBEGIN)
{
BOOL bResult;
UINT tmpLen;
m_state = S_RESET;
lstrcpy(m_szBuffer, "ACK\r\n");
TRACE("Server sending first ACK\n");
tmpLen = lstrlen( m_szBuffer) + 1; // start with an ACK
bResult = Send((LPCSTR) m_szBuffer, tmpLen);
if (bResult != TRUE)
{
CallBack(WSCHILDCLOSED, MyLastError());
CancelIO();
DestroySocket();
delete this;
}
else
m_state = S_ACK1;
}
}
void CTalkServer::OnReceiveCompleted(int iEvent, int iError)
{
// A Winsock receive operation has completed.
// Handle protocol states requirements here
//
if (iError == 0)
{
switch(m_state)
{
case S_READSIZE:
// get the audio buffer size to receive
m_szBuffer[LastRecvLength()] = '\0';
TRACE("Server got ->%s<-\n", m_szBuffer);
m_bufSize = atol(&m_szBuffer[4]);
TRACE("Server expect size = ->%lu<-\n", m_bufSize);
SendAck();
break;
case S_READTYPE:
// get the audio data type to receive, we only accept 1 type
m_szBuffer[LastRecvLength()] = '\0';
m_compressionType = atoi(&m_szBuffer[4]);
TRACE("Server got ->%s<-\n", m_szBuffer);
SendAck();
break;
case S_READDATA:
// keep reading in small chunks until all audio data are received
if (DataRead() == DONE)
{
TRACE("Server has completed all data read, sending final ack!\n");
SendAck();
}
break;
}
}
else
{
TRACE("Disconnect from error code %d\n", iError);
CallBack(WSCHILDCLOSED, iError);
CancelIO();
DestroySocket();
delete this;
}
}
void CTalkServer::OnSendCompleted(int iEvent, int iError)
{
// Winsock Send operation completed.
// We handle protocol requirements and transition protocol states
// here.
if (iError == 0)
{
BOOL bResult;
// got an ack sent
switch (m_state)
{
case S_ACK1:
case S_READTYPE:
case S_READSIZE:
// simplified coding: DATACHUNKSIZE is actually too large for
// S_ACK1 and S_READTYPE states, but it works okay as maximum size
bResult = Recv((LPSTR) m_szBuffer, DATACHUNKSIZE);
if (bResult != TRUE)
{
CallBack(WSCHILDCLOSED, MyLastError());
CancelIO();
DestroySocket();
delete this;
}
else
{
// advance to next state
m_state = ((m_state == S_ACK1) ? S_READTYPE :
((m_state == S_READTYPE) ? S_READSIZE : S_READDATA));
}
break;
case S_READDATA:
// final ack, can start processing the data
// if we want here; we'll just set a flag, and send voice
// when client disconnects
TRACE("Server complete sending final ack!\n");
m_speechReady = TRUE;
break;
} // of switch
} //of iError = 0
else
{
CallBack(WSCHILDCLOSED, iError);
CancelIO();
DestroySocket();
delete this;
}
}
UINT CTalkServer::DataRead()
{
// got another buffer
BOOL bResult;
int RecvSize = LastRecvLength();
ASSERT(RecvSize > 0);
_fmemcpy( m_pCurBuf, m_szBuffer, RecvSize);
m_pCurBuf += RecvSize;
if (((ULONG)(m_pCurBuf - m_pBuffer)) < m_bufSize) // need more
{
bResult = Recv((LPSTR) m_szBuffer, DATACHUNKSIZE);
if (bResult != TRUE)
{
CallBack(WSCHILDCLOSED, MyLastError());
CancelIO();
DestroySocket();
delete this;
}
else
return TRUE;
}
else // don't need any more
{
return (DONE);
}
return TRUE; // never reach here anyway; surpress compiler warning
}
void CTalkServer::SendAck()
{
BOOL bResult;
UINT tmpLen;
// Routine to send an ACK
//
// start with an ACK
lstrcpy(m_szBuffer, "ACK\r\n");
TRACE("In Server sending ack = ->%s<-\n", m_szBuffer);
tmpLen = lstrlen( m_szBuffer) + 1; // send only enough for compatibility w UNIX
bResult = Send((LPCSTR) m_szBuffer, tmpLen);
if (bResult != TRUE)
{
CallBack(WSCHILDCLOSED, MyLastError());
CancelIO();
DestroySocket();
delete this;
}
}
void CTalkServer::OnDisconnected(int iEvent, int iError)
{
if (m_speechReady == TRUE)
{
// send voice to output
// PhoneView will free the buffer after playback
m_PhoneView->ExpandWaveOut( m_hspeechBuffer, m_pBuffer, m_bufSize);
}
CallBack(WSCHILDCLOSED, iError);
Notify(m_socket, WSCHILDCLOSED, iError);
CancelIO();
DestroySocket();
delete this;
}
/////////////////////////////////////////////////////////////////////////////
// CTalkServer diagnostics
#ifdef _DEBUG
void CTalkServer::AssertValid() const
{
CSocketOwner::AssertValid();
}
void CTalkServer::Dump(CDumpContext& dc) const
{
CSocketOwner::Dump(dc);
}
#endif //_DEBUG
////////////////////////////////////////////////////////////////////////
// CTalkClient
//
// Philosophy of Design: CONSERVATIVE
//
// terminate and destroy connection, socket, and client on any error
//
// Preferred Philosophy of Design: LIBERAL
// flow the protocol, let timer detect failure retry and disconnect; this
// is not implemented here
//
CTalkClient::CTalkClient()
{
}
CTalkClient::~CTalkClient()
{
}
WSSOCKET CTalkClient::Connect(
UINT prot, WSADDRESS iaddress, WSPORTID iPort, UINT uInterval,
HWND hCallBack, NOTIFYPROC fNotify, UINT uLimit, HPSTR talkBuffer, ULONG bufLen)
{
// make connection and start protocol, UDP is never used currently
int iProtocol = GetProtocolByName( (prot == TCPSOCK)? "tcp":"udp");
if (iPort == 0)
iPort=GetServiceByName("intertalk", (prot == TCPSOCK)? "tcp":"udp");
m_socket = CreateSocket(prot, iProtocol,0, sizeof(LPSTR), hCallBack,
UniversalCSockOwnerNotify);
if (m_socket != NOTASOCK)
{
// Store a pointer to myself (this object) with the socket
// associated memory; this allows all socket related messages
// to find their way back to us.
CTalkClient * __far * pto = (CTalkClient * __far *) GetSockMemory();
*pto = this;
m_iType = WS_TALKCLIENT;
// for udp sockets, the address is required
// if (prot == UDPSOCK)
// m_lAddress = GetHostByName(lpName);
// else
// m_lAddress = INADDR_NONE;
// m_uPort = iPort;
// m_lRecvAddress = INADDR_NONE;
// m_uSequence = 0;
// m_bInProgress = FALSE;
// m_uInterval= (uInterval < MIN_UINTERVAL) ? MIN_UINTERVAL: uInterval;
// m_uLimit = uLimit;
m_fNotifyProc = fNotify;
m_bConnected = (prot == TCPSOCK) ? TRUE: FALSE;
m_talkBuffer = talkBuffer;
m_pCurBuf = talkBuffer;
m_bufLen = bufLen;
if (m_bConnected == TRUE)
{
if(ConnectTo(/* MyAddress() */ iaddress, iPort) != TRUE)
{
Notify(m_socket, WSCONNECTED, 998);
DestroySocket();
m_socket = NOTASOCK;
} // of if connect
}
else
Notify(m_socket, WSCONNECTED, 0);
} // of if (m_socket != NOTASOCK)
return (m_socket);
} // of ConnectByName
void CTalkClient::OnConnected(int iEvent, int iError)
{
// Connect completed, we now have a TCP connection. Start the
// protocol by waiting for the initial ACK on a Recv.
if (1) //iError == 0)
{
if (CreateTimer(m_uInterval) == TRUE)
{
BOOL bResult;
if(m_bConnected == TRUE)
bResult = Recv((LPSTR) &m_szBuffer, DATACHUNKSIZE);
if (bResult == FALSE)
{
m_state = S_RESET;
m_bInProgress = FALSE;
}
else
{
m_state = S_ACK1;
m_bInProgress = TRUE;
}
} // of CreateTimer == TRUE
else
{ // unable to create Timer
m_bInProgress = FALSE;
CallBack(WSTALKFAILED, 0);
}
} // on iError == 0
else
// not connected
{
m_bInProgress = FALSE;
}
CallBack(WSCONNECTED, iError);
}
void CTalkClient::OnSendCompleted(int iEvent, int iError)
{
// A Winsok Send operation has completed, handle the
// protocol requirements. Usually by waiting for an ACK.
// See Dr. Dobb's Journal text for description of protocol.
if (iError == 0)
{
UINT bStatus;
switch(m_state)
{
case S_READTYPE:
case S_READSIZE:
TRACE("In SEND TYPE OR SEND SIZE\n");
GetAck();
break;
case S_READDATA:
TRACE("In SEND DATA PHASE\n");
bStatus = SendData();
if (bStatus == FALSE) // something went wrong
{
TRACE("Something Went wrong in Send Data phase.\n");
m_bInProgress = FALSE;
m_state = S_RESET;
}
if (bStatus == DONE)
{
TRACE("Send Data Completed! Wait for ACK!\n");
GetAck(); // get final ack
}
break;
}
}
else
{
m_uErrorCount++;
m_bInProgress = FALSE;
}
}
void CTalkClient::OnReceiveCompleted(int iEvent, int iError)
{
BOOL bResult;
// A Winsock Recv opertion has completed. Handle the protocol
// requirement and mark the state transitions.
//
if (1)//(iError == 0)
{
UINT tmpLen;
switch(m_state)
{
case S_ACK1: // send the type
m_szBuffer[LastRecvLength()]='\0';
TRACE("Client got %s\n", m_szBuffer);
lstrcpy(m_szBuffer, "002 1\r\n");
tmpLen = lstrlen( m_szBuffer) + 1; // send only enough for compatibility w UNIX
bResult = Send((LPCSTR) m_szBuffer, tmpLen);
if (bResult != TRUE)
{
CallBack(WSTALKFAILED, 0);
m_bInProgress == FALSE;
m_state = S_RESET;
OnDisconnected(iEvent, iError);
}
else
m_state = S_READTYPE;
break;
case S_READTYPE: // send the size
m_szBuffer[LastRecvLength()]='\0';
TRACE("Client got %s\n", m_szBuffer);
sprintf( m_szBuffer,"003 %lu\r\n", m_bufLen);
TRACE("total COMPRESSED SIZE is: %lu\n", m_bufLen);
tmpLen = lstrlen(m_szBuffer) + 1; // send just enough
bResult = Send((LPCSTR) m_szBuffer, tmpLen);
if (bResult != TRUE)
{
CallBack(WSTALKFAILED, 0);
m_bInProgress == FALSE;
m_state = S_RESET;
OnDisconnected(iEvent, iError);
}
else
m_state = S_READSIZE;
break;
case S_READSIZE: // send the data
m_szBuffer[LastRecvLength()]='\0';
TRACE("Client got %s\n", m_szBuffer);
// in case there's less data to sent than 1 block
m_lastSentSize = (m_bufLen > (ULONG) DATACHUNKSIZE) ? DATACHUNKSIZE : (UINT) m_bufLen;
TRACE("Sending %d bytes...\n", m_lastSentSize);
if (m_bConnected == TRUE)
bResult = Send((LPCSTR) m_pCurBuf, m_lastSentSize);
else
bResult = SendTo((LPCSTR) m_pCurBuf, m_lastSentSize, m_lAddress, m_uPort);
if (bResult == FALSE)
{
CallBack(WSTALKFAILED, 0);
m_bInProgress = FALSE;
m_uErrorCount++;
OnDisconnected(iEvent, iError);
}
else
{
m_bInProgress = TRUE;
m_state = S_READDATA;
}
break;
case S_READDATA: // done, got final ack, wrap up
TRACE("it is FINAL, got last ack, We're wrapping up.\n");
OnDisconnected(iEvent, iError);
break;
} // of case
} // of iError==0
else
{
CallBack(WSTALKFAILED, 0);
m_uErrorCount++;
m_bInProgress = FALSE;
OnDisconnected(iEvent, iError);
}
}
UINT CTalkClient::SendData()
{
// break up the large block of data into multiple small sends
// to fit through the max buffer size allowed by Winsock
m_pCurBuf += m_lastSentSize; // mark another chunk sent
if (m_pCurBuf < (m_talkBuffer + m_bufLen)) // NOT everything sent
{
BOOL bResult;
m_uAttempts++;
UINT tmpval = (UINT)((m_talkBuffer + m_bufLen) - m_pCurBuf);
// potentially send last remaining block
m_lastSentSize = (tmpval > DATACHUNKSIZE) ? DATACHUNKSIZE : tmpval;
TRACE("ACTUALLY Sending %d values\n", m_lastSentSize);
if (m_bConnected == TRUE)
bResult = Send((LPCSTR) m_pCurBuf, m_lastSentSize);
if (bResult == FALSE)
{
m_bInProgress = FALSE;
m_uErrorCount++;
return FALSE;
}
else
{
m_bInProgress = TRUE;
return TRUE;
}
} // not everything send
else
return (DONE);
}
void CTalkClient::GetAck()
{
BOOL bResult;
m_lRecvAddress = INADDR_NONE;
if(m_bConnected == TRUE)
bResult = Recv((LPSTR) &m_szBuffer, DATACHUNKSIZE);
else
bResult = RecvFrom((LPSTR) &m_szBuffer, WSTALKBUFFERSIZE,
(LPWSADDRESS) &m_lRecvAddress, (LPWSPORTID) &m_uRecvPort);
if (bResult == FALSE)
{
TRACE("Inside Get Ack, something went wrong!\n");
m_state = S_RESET;
m_bInProgress = FALSE;
}
else
{
m_bInProgress = TRUE;
}
}
void CTalkClient::OnTimerExpired(int iEvent, int iError)
{
// Timer is not used currently. Definitely can be used to
// make the protocol more robust by re-trying and/or
// aborting after time-out, etc.
if (KillTimer() == TRUE)
{ // timed out
}
CallBack(WSTALKBACK, 0);
}
void CTalkClient::OnDisconnected(int iEvent, int iError)
{
KillTimer();
m_bInProgress = FALSE;
TRACE("Inside OnDisconnected!\n");
CancelIO();
CallBack(WSDISCONNECTED, iError);
DestroySocket(); // kill it off
}
BOOL CTalkClient::End()
{
BOOL bResult;
if (m_iType == WS_TALKCLIENT)
bResult = DestroySocket();
else
bResult = FALSE;
return bResult;
}
/////////////////////////////////////////////////////////////////////////////
// CSocketClient diagnostics
#ifdef _DEBUG
void CTalkClient::AssertValid() const
{
CSocketOwner::AssertValid();
}
void CTalkClient::Dump(CDumpContext& dc) const
{
CSocketOwner::Dump(dc);
}
#endif //_DEBUG
////////////////////////////////////////////////////////////////////////
// CTalkListenServer
//
// Listen at the well-known port. Upon foregin connection, dynamically
// create a Server object, allocate memory, and start the server thread's
// protocol handler.
//
CTalkListenServer::CTalkListenServer()
{
// TODO: add member initialization code here
}
CTalkListenServer::~CTalkListenServer()
{
}
WSSOCKET CTalkListenServer::StartListener(LPCSTR lpName,
WSPORTID Port, LPWSADDRESS ConnAddr, HWND hCallBack, NOTIFYPROC fCallBack, CPhoneView * phView)
{
m_PhoneView = phView;
return (CSockListenServer::StartListener(lpName, Port, ConnAddr, hCallBack, fCallBack));
}
void CTalkListenServer::OnOtherMsgs(int iEvent, int iError)
{
// override the virtual 'catch-all' and intercepts the Accept() message
// to spin-off our Protocol specific Server process
//
if (iEvent == WSACCEPTCOMPLETE && (WSSOCKET)iError != NOTASOCK)
{
WSSOCKET iNewSocket = AttachSocket((HSOCK)iError, (int) sizeof(LPSTR),
MyCallBack(),
(NOTIFYPROC) UniversalCSockOwnerNotify);
if (iNewSocket != NOTASOCK) // attach sucessful
{
CTalkServer * nuServer = new(CTalkServer);
if (nuServer != NULL)
{
nuServer->SetSafeSocket(iNewSocket);
nuServer->m_PhoneView = m_PhoneView;
if (nuServer->GetBuffer() == TRUE) // try to allocate full speech Buffer 1st
{
CTalkServer * __far * optr = (CTalkServer * __far *) nuServer->GetSockMemory();
if (optr != NULL)
{
*optr = nuServer;
nuServer->m_fNotifyProc = m_fNotifyProc;
nuServer->m_iType = WSTALKCHILD;
nuServer->Notify(iNewSocket, WSTALKBEGIN, 0);
CallBack( WSTALKNEWCHILD, (int) iNewSocket);
}
else
{
Notify(m_socket, WSTALKLISFAILED, IGP_NOMEM);
nuServer->DestroySocket();
delete nuServer;
}
} // GetBuffer()
else
{
Notify(m_socket, WSTALKLISFAILED, IGP_NOMEM);
nuServer->DestroySocket();
delete nuServer;
}
} // of nuServer != NULL
else
{
Notify(m_socket, WSTALKLISFAILED, IGP_NOMEM);
WSDestroySocket(iNewSocket);
}
} // iNewSocket
} // of iEvent
}
/////////////////////////////////////////////////////////////////////////////
// CTalkListenServer diagnostics
#ifdef _DEBUG
void CTalkListenServer::AssertValid() const
{
CSocketOwner::AssertValid();
}
void CTalkListenServer::Dump(CDumpContext& dc) const
{
CSocketOwner::Dump(dc);
}
#endif //_DEBUG